Debugging Notebooks

Naive Way - print


In [1]:
import random
def find_max (values):
    max = 0
    print(f"Initial max is {max}")
    for val in values:
        if val > max:
            max = val
    return max

sample = random.sample(range(100), 10)
find_max(sample)


Initial max is 0
Out[1]:
98

Advantages

  • Easy
  • No installation required

Disadvantages:

  • Hard to pinpoint error prown locations
  • Can be spammy

Classical Way - PDB

PDB is used in the following ways:

  • using "breakpoint()" since python 3.7.
  • using "from IPython.core.debugger import set_trace;set_trace()" for python notebooks.
  • using "import pdb; pdb.set_trace()".

In [1]:
import random
def find_max (values):
    max = 0
    import pdb; pdb.set_trace()
    for val in values:
        if val > max:
            max = val
    return max

sample = random.sample(range(100), 10)
find_max(sample[:-3])


> <ipython-input-1-32c173314568>(5)find_max()
-> for val in values:
> <ipython-input-1-32c173314568>(10)<module>()
-> find_max(sample)
  5  	    for val in values:
  6  	        if val > max:
  7  	            max = val
  8  	    return max
  9  	sample = random.sample(range(100), 10)
 10  ->	find_max(sample)
[EOF]
[60, 33, 77, 76, 98, 47, 38, 8, 91, 26]
---------------------------------------------------------------------------
BdbQuit                                   Traceback (most recent call last)
<ipython-input-1-32c173314568> in <module>
      8     return max
      9 sample = random.sample(range(100), 10)
---> 10 find_max(sample)

<ipython-input-1-32c173314568> in find_max(values)
      3     max = 0
      4     import pdb; pdb.set_trace()
----> 5     for val in values:
      6         if val > max:
      7             max = val

<ipython-input-1-32c173314568> in find_max(values)
      3     max = 0
      4     import pdb; pdb.set_trace()
----> 5     for val in values:
      6         if val > max:
      7             max = val

~/my-envs/research/lib/python3.7/bdb.py in trace_dispatch(self, frame, event, arg)
     86             return # None
     87         if event == 'line':
---> 88             return self.dispatch_line(frame)
     89         if event == 'call':
     90             return self.dispatch_call(frame, arg)

~/my-envs/research/lib/python3.7/bdb.py in dispatch_line(self, frame)
    111         if self.stop_here(frame) or self.break_here(frame):
    112             self.user_line(frame)
--> 113             if self.quitting: raise BdbQuit
    114         return self.trace_dispatch
    115 

BdbQuit: 

A nice cheatsheet:

Advantages

  • No installation required
  • Dynamic
  • Mature:
    • More features
    • Documentation (Stackoverflow ftw)
    • Less bugs

Disadvantages:

  • Very scary
  • Learning curve

Additional resources:

Better Way Jupyter Notebooks - pixie_debugger


In [12]:
import contextlib

with contextlib.redirect_stdout(None):
    import pixiedust

In [15]:
%%pixie_debugger
import random
def find_max (values):
    max = 0
    for val in values:
        if val > max:
            max = val
    return max
find_max(random.sample(range(100), 10))


Advantages

  • Easy
  • Dynamic

Disadvantages:

  • Additional installation required
  • Not mature:
    • Documentation
    • Supported evaluation
    • Working on Jupyter notebooks but not in jupyterlab

Setup:

  • pip install pixiedust

Additional resources:

Better Way Jupyter lab - xpython


In [2]:
import random
def find_max (values):
    max = 0
    for val in values:
        if val > max:
            max = val
    return max
find_max(random.sample(range(100), 10))


Out[2]:
99

Advantages

  • Easy
  • Dynamic
  • No additional code

Disadvantages:

  • Additional installation required
  • Require diffrent interperter
  • Not mature:
    • Documentation
    • No evaluation change
    • Installation problematic for some environments

Setup:

For each conda environment:

  • conda install -y -c conda-forge xeus-python=0.6.12 notebook=6 ptvsd
  • jupyter labextension install @jupyterlab/debugger